home *** CD-ROM | disk | FTP | other *** search
/ Hardcore Visual Basic 5.0 (2nd Edition) / Hardcore Visual Basic 5.0 - Second Edition (1997)(Microsoft Press).iso / Code / LOCALM~1 / Sort.bas < prev    next >
BASIC Source File  |  1997-06-14  |  11KB  |  288 lines

  1. Attribute VB_Name = "MSort"
  2. Option Explicit
  3.  
  4. Public Enum EErrorSort
  5.     eeBaseSort = 13620  ' Sort
  6. End Enum
  7.  
  8. ' Iterative QuickSort algorithm
  9. Sub SortArray(aTarget() As Variant, Optional vFirst As Variant, _
  10.               Optional vLast As Variant, Optional helper As ISortHelper)
  11.     Dim iFirst As Long, iLast As Long
  12.     If IsMissing(vFirst) Then iFirst = LBound(aTarget) Else iFirst = vFirst
  13.     If IsMissing(vLast) Then iLast = UBound(aTarget) Else iLast = vLast
  14.     If helper Is Nothing Then Set helper = New CSortHelper
  15.     
  16. With helper
  17.     Dim iLo As Long, iHi As Long, iRand As Long, stack As New CStack
  18.     Do
  19.         Do
  20.             ' Swap from ends until first and last meet in the middle
  21.             If iFirst < iLast Then
  22.                 ' If we're in the middle and out of order, swap
  23.                 If iLast - iFirst = 1 Then
  24.                     If .Compare(aTarget(iFirst), aTarget(iLast)) > 0 Then
  25.                         .Swap aTarget(iFirst), aTarget(iLast)
  26.                     End If
  27.                 Else
  28.                     ' Split at some random point
  29.                     .Swap aTarget(iLast), _
  30.                           aTarget(MRandom.Random(iFirst, iLast))
  31.                     ' Swap high values below the split for low values above
  32.                     iLo = iFirst: iHi = iLast
  33.                     Do
  34.                         ' Find any low value larger than split
  35.                         Do While (iLo < iHi) And _
  36.                                  (.Compare(aTarget(iLo), aTarget(iLast)) <= 0)
  37.                             iLo = iLo + 1
  38.                         Loop
  39.                         ' Find any high value smaller than split
  40.                         Do While (iHi > iLo) And _
  41.                                  (.Compare(aTarget(iHi), aTarget(iLast)) >= 0)
  42.                             iHi = iHi - 1
  43.                         Loop
  44.                         ' Swap too high low value for too low high value
  45.                         If iLo < iHi Then .Swap aTarget(iLo), aTarget(iHi)
  46.                     Loop While iLo < iHi
  47.                     ' Current (iLo) is larger than split (iLast), so swap
  48.                     .Swap aTarget(iLo), aTarget(iLast)
  49.                     ' Push range markers of larger part for later sorting
  50.                     If (iLo - iFirst) < (iLast - iLo) Then
  51.                         stack.Push iLo + 1
  52.                         stack.Push iLast
  53.                         iLast = iLo - 1
  54.                     Else
  55.                         stack.Push iFirst
  56.                         stack.Push iLo - 1
  57.                         iFirst = iLo + 1
  58.                     End If
  59.                     ' Exit from inner loop to process smaller part
  60.                     Exit Do
  61.                 End If
  62.             End If
  63.             
  64.             ' If stack empty, Exit outer loop
  65.             If stack.Count = 0 Then Exit Sub
  66.             ' Else pop first and last from last deferred section
  67.             iLast = stack.Pop
  68.             iFirst = stack.Pop
  69.         Loop
  70.     Loop
  71. End With
  72. End Sub
  73.  
  74. ' QuickSort algorithm
  75. Sub SortCollection(nTarget As Collection, Optional vFirst As Variant, _
  76.                    Optional vLast As Variant, _
  77.                    Optional helper As ISortHelper)
  78.     Dim iFirst As Long, iLast As Long
  79.     If IsMissing(vFirst) Then iFirst = 1 Else iFirst = vFirst
  80.     If IsMissing(vLast) Then iLast = nTarget.Count Else iLast = vLast
  81.     If helper Is Nothing Then Set helper = New CSortHelper
  82.     
  83. With helper
  84.     Dim iLo As Long, iHi As Long, stack As New CStack
  85.     Do
  86.         Do
  87.             ' Swap from ends until first and last meet in the middle
  88.             If iFirst < iLast Then
  89.                 ' If we're in the middle and out of order, swap
  90.                 If iLast - iFirst = 1 Then
  91.                     If .Compare(nTarget(iFirst), nTarget(iLast)) > 0 Then
  92.                         .CollectionSwap nTarget, iFirst, iLast
  93.                     End If
  94.                 Else
  95.                     ' Split at some random point
  96.                     .CollectionSwap nTarget, iLast, _
  97.                                     MRandom.Random(iFirst, iLast)
  98.                     ' Swap high values below the split for low values above
  99.                     iLo = iFirst: iHi = iLast
  100.                     Do
  101.                         ' Find find any low value larger than split
  102.                         Do While (iLo < iHi) And _
  103.                                  (.Compare(nTarget(iLo), nTarget(iLast)) <= 0)
  104.                             iLo = iLo + 1
  105.                         Loop
  106.                         ' Find any high value smaller than split
  107.                         Do While (iHi > iLo) And _
  108.                                  (.Compare(nTarget(iHi), nTarget(iLast)) >= 0)
  109.                             iHi = iHi - 1
  110.                         Loop
  111.                         ' Swap too high low value for too low high value
  112.                         If iLo < iHi Then .CollectionSwap nTarget, iLo, iHi
  113.                     Loop While iLo < iHi
  114.                     ' Current (iLo) is larger than split (iLast), so swap
  115.                     .CollectionSwap nTarget, iLo, iLast
  116.                     ' Push range markers of larger part for later sorting
  117.                     If (iLo - iFirst) < (iLast - iLo) Then
  118.                         stack.Push iLo + 1
  119.                         stack.Push iLast
  120.                         iLast = iLo - 1
  121.                     Else
  122.                         stack.Push iFirst
  123.                         stack.Push iLo - 1
  124.                         iFirst = iLo + 1
  125.                     End If
  126.                     ' Exit from inner loop to process smaller part
  127.                     Exit Do
  128.                 End If
  129.             End If
  130.             
  131.             ' If stack empty, Exit outer loop
  132.             If stack.Count = 0 Then Exit Sub
  133.             ' Else pop first and last from last deferred section
  134.             iLast = stack.Pop
  135.             iFirst = stack.Pop
  136.         Loop
  137.     Loop
  138. End With
  139. End Sub
  140.  
  141. Function BSearchArray(av() As Variant, ByVal vKey As Variant, _
  142.                       iPos As Long, _
  143.                       Optional helper As ISortHelper) As Boolean
  144.     Dim iLo As Long, iHi As Long
  145.     Dim iComp As Long, iMid As Long
  146.     If helper Is Nothing Then Set helper = New CSortHelper
  147.     
  148.     iLo = LBound(av): iHi = UBound(av)
  149.     Do
  150.         iMid = iLo + ((iHi - iLo) \ 2)
  151.         iComp = helper.Compare(av(iMid), vKey)
  152.         Select Case iComp
  153.         Case 0
  154.             ' Item found
  155.             iPos = iMid
  156.             BSearchArray = True
  157.             Exit Function
  158.         Case Is > 0
  159.             ' Item is in lower half
  160.             iHi = iMid - 1
  161.             If iHi < iLo Then Exit Do
  162.         Case Is < 0
  163.             ' Item is in upper half
  164.             iLo = iMid + 1
  165.             If iLo > iHi Then Exit Do
  166.         End Select
  167.     Loop
  168.     ' Item not found, but return position to insert
  169.     iPos = iMid - (iComp < 0)
  170.         
  171. End Function
  172.  
  173. ' BSearchCollection performs a binary search on a collection and
  174. ' returns True or False depending on whether the search item is
  175. ' found. BSearchCollection also returns the index of the search
  176. ' item in iPos. If the item isn't found, iPos will contain the
  177. ' index that the item should occupy in the collection. Note that
  178. ' iPos will equal 1 if the collection is empty, and will equal
  179. ' n.Count + 1 if the search item should be inserted at the end
  180. ' of the collection.
  181. '
  182. ' The following example uses BSearchCollection to insert an item
  183. ' in sorted order:
  184. '
  185. '    Dim n as new Collection, v As Variant, iPos As Long
  186. '
  187. '    v = InputBox("Collection item to insert: ")
  188. '    ' Insert item in collection if item doesn't already exist
  189. '    If Not BSearchCollection(n, v, iPos) Then
  190. '        On Error GoTo IndexError
  191. '        ' The following line of code generates an error if the
  192. '        ' collection is empty or iPos > n.Count. In either case,
  193. '        ' the error handler adds the item to the end of the collection
  194. '        n.Add v, , iPos
  195. '    End If
  196. '
  197. '    Exit Sub
  198. 'IndexError:
  199. '    ' Item needs to be inserted at end of collection
  200. '    n.Add v
  201.  
  202. Function BSearchCollection(n As Collection, ByVal vKey As Variant, _
  203.                            iPos As Long, _
  204.                            Optional helper As ISortHelper) As Boolean
  205.     Dim iLo As Long, iHi As Long
  206.     Dim iComp As Long, iMid As Long
  207.     If helper Is Nothing Then Set helper = New CSortHelper
  208.     
  209.     ' Special case if empty collection
  210.     If n.Count = 0 Then
  211.         iPos = 1
  212.         Exit Function
  213.     End If
  214.     
  215.     iLo = 1: iHi = n.Count
  216.     Do
  217.         iMid = iLo + ((iHi - iLo) \ 2)
  218.         iComp = helper.Compare(n(iMid), vKey)
  219.         Select Case iComp
  220.         Case 0
  221.             ' Item found
  222.             iPos = iMid
  223.             BSearchCollection = True
  224.             Exit Function
  225.         Case Is > 0
  226.             ' Item is in lower half
  227.             iHi = iMid - 1
  228.             If iHi < iLo Then Exit Do
  229.         Case Is < 0
  230.             ' Item is in upper half
  231.             iLo = iMid + 1
  232.             If iLo > iHi Then Exit Do
  233.         End Select
  234.     Loop
  235.     ' Item not found, but return position to insert
  236.     iPos = iMid - (iComp < 0)
  237.     
  238. End Function
  239.  
  240. Sub ShuffleArray(av() As Variant, Optional helper As ISortHelper)
  241.     Dim iFirst As Long, iLast As Long
  242.     If helper Is Nothing Then Set helper = New CSortHelper
  243.     
  244.     iFirst = LBound(av): iLast = UBound(av)
  245.     ' Randomize array
  246.     Dim i As Long, v As Variant, iRnd As Long
  247.     For i = iLast To iFirst + 1 Step -1
  248.         ' Swap random element with last element
  249.         iRnd = MRandom.Random(iFirst, i)
  250.         helper.Swap av(i), av(iRnd)
  251.     Next
  252. End Sub
  253.  
  254. Sub ShuffleCollection(n As Collection, Optional helper As ISortHelper)
  255.     Dim iFirst As Long, iLast As Long
  256.     If helper Is Nothing Then Set helper = New CSortHelper
  257.     
  258.     iFirst = 1: iLast = n.Count
  259.     ' Randomize collection
  260.     Dim i As Long, v As Variant, iRnd As Long
  261.     For i = iLast To iFirst + 1 Step -1
  262.         ' Swap random element with last element
  263.         iRnd = MRandom.Random(iFirst, i)
  264.         helper.CollectionSwap n, i, iRnd
  265.     Next
  266. End Sub
  267.  
  268. #If fComponent = 0 Then
  269. Private Sub ErrRaise(e As Long)
  270.     Dim sText As String, sSource As String
  271.     If e > 1000 Then
  272.         sSource = App.ExeName & ".Sort"
  273.         Select Case e
  274.         Case eeBaseSort
  275.             BugAssert True
  276.        ' Case ee...
  277.        '     Add additional errors
  278.         End Select
  279.         Err.Raise COMError(e), sSource, sText
  280.     Else
  281.         ' Raise standard Visual Basic error
  282.         sSource = App.ExeName & ".VBError"
  283.         Err.Raise e, sSource
  284.     End If
  285. End Sub
  286. #End If
  287.  
  288.